package OldCode;
/*
 * Created on Oct 17, 2003
 *
 * To change the template for this generated file go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;//import javax.swing.*;

/**
 * @author sszhao
 *
 * To change the template for this generated type comment go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
public class SunburstMenuNoExpansionRotate extends JPanel {

	//I will keep these constants for now, although some of them might not
	//be useful, but I might need to use them later

	//===========================================================================
	//===   CONSTANTS   =========================================================

	public static final boolean DEBUG = true;

	/** Where the first pie slice is rendered. Currently North.  */
	public static final double DEFAULT_START = Math.PI / 2;

	/** Default radius size for the pie menu, currently 100.  */
	public static final int DEFAULT_BIG_RADIUS = 130;

	/** The radius of the small inner circle, currently 20.  */
	public static final int DEFAULT_SMALL_RADIUS = 20;

	/**
	 * The default delay (msec) before pie menus initially pop up, currently
	 * 200ms. Don't make this too small, or you'll get strange errors.
	 */
	public static final long DEFAULT_INITIAL_DELAY = 200;

	/** The default delay (msec) before pie submenus pop up, currently 500ms.  */
	public static final long DEFAULT_SUBMENU_DELAY = 500;

	/** Default value for auto-open is false.  */
	public static final boolean DEFAULT_AUTO_OPEN = false;

	/** Default value for clipping is false.  */
	public static final boolean DEFAULT_CLIP_FLAG = false;

	/**
	 * The default scaling factor for where to draw objects.
	 * This scaling factor is multiplied with the radius to determine
	 * where to draw things. Currently 0.65.
	 */
	public static final double DEFAULT_SCALING_FACTOR = 0.65;

	//-----------------------------------------------------------------

	/** The default color of the pie menu, currently light gray.  */
	public static final Color DEFAULT_FILL_COLOR = new Color(204, 204, 204);
	;

	/** The default color of the lines in the pie menu, currently black.  */
	public static final Color DEFAULT_LINE_COLOR =
		new java.awt.Color(0, 0, 0, 0.35f);

	/**
	 * The default color of the selected item in the pie menu, currently gray.
	 */
	public static final Color DEFAULT_SELECTED_COLOR = new Color(167, 167, 167);

	/** The default color of fonts, currently black.  */
	public static final Color DEFAULT_FONT_COLOR = Color.black;

	/** The default font, currently sans serif plain, 15 point.  */
	public static final Font DEFAULT_FONT =
		new Font("SansSerif", Font.PLAIN, 12);

	public static final Font DEFAULT_MARKING_FONT =
		new Font("SansSerif", Font.BOLD, 10);

	/** Default line width for drawing lines in the Pie Menu, currently 0.7.  */
	public static final float DEFAULT_LINE_WIDTH = 0.7f;

	//-----------------------------------------------------------------

	private static final int STATE_TAP_OPEN = 90;
	private static final int STATE_TAPHOLD_OPEN = 91;
	private static final int STATE_DRAG_OPEN = 92;

	private static final int ORIENT_TOP = 1;
	private static final int ORIENT_TOPRIGHT = 2;
	private static final int ORIENT_BOTTOM = 3;
	private static final int ORIENT_TOPLEFT = 4;
	private static final int ORIENT_BOTRIGHT = 5;
	private static final int ORIENT_BOTLEFT = 6;

	//===   CONSTANTS   =========================================================
	//===========================================================================

	//===========================================================================
	//===   CLASS METHODS FOR PIE MENU DEFAULTS   ===============================

	//// State tracking - turns submenus and select on or off globally.
	//static boolean enableSubmenus = true;
	static boolean enableSelect = true;

	//// Global behavior of all pie menus
	static boolean defaultAutoOpen = DEFAULT_AUTO_OPEN;
	static int defaultOpenState = STATE_TAP_OPEN;
	static boolean defaultRelocateSubmenusFlag = true;
	static long defaultInitialDelay = DEFAULT_INITIAL_DELAY;
	static long defaultSubmenuDelay = DEFAULT_SUBMENU_DELAY;
	static boolean defaultFlagPenMode = false;

	//// Appearance defaults
	static Color defaultFillColor = DEFAULT_FILL_COLOR;
	static Color defaultLineColor = DEFAULT_LINE_COLOR;
	static Color defaultSelectedColor = DEFAULT_SELECTED_COLOR;
	static Color defaultFontColor = DEFAULT_FONT_COLOR;
	static Font defaultFont = DEFAULT_FONT;
	static float defaultLineWidth = DEFAULT_LINE_WIDTH;
	static int defaultBigRadius = DEFAULT_BIG_RADIUS;
	static int defaultSmallRadius = DEFAULT_SMALL_RADIUS;
	static double defaultScalingFactor = DEFAULT_SCALING_FACTOR;
	static boolean defaultClipFlag = DEFAULT_CLIP_FLAG;
	static boolean defaultFlagLineNorth = false;
	static Image defaultSubmenuIcon = null;

	//===========================================================================
	//===   NONLOCAL VARIABLES   ================================================

	//// PieMenu appearance variables
	Color fillColor; // color of filled background
	Color lineColor; // color of lines
	Color fontColor; // color of font
	Color selectedColor; // the color of selected item
	Color defaultSelectedItemColor; // translucent selectedColor
	Font font; // the font to use
	boolean flagLineNorth; // draw line to north?
	//BlinkTimer        timer;                       // timer for blinking

	transient Image submenuIconImage; // arrow image for submenus
	transient BasicStroke stroke; // stroke characteristics

	//// Event handling variables
	SunburstMenuListener lstnr; // mouse listener on ourself
	ItemListener clistener; // listens to the parent
	MouseEvent lastEvent; // last mouse event occurring
	//PieMenuHandler    handler;                     // actually handles events
	boolean flagDraggedInPieMenu; // did we drag in the pie
	//   menu? If so, don't
	//   let the left-mouse
	//   button close the pie
	//   menu. Let it activate
	//   an item instead.
	boolean flagJustOpened; // there are problems with
	//   tap-hold, since if you
	//   release the button
	//   immediately after
	//   opening, the pie menu
	//   closes, which is not the
	//   behavior you want.
	private boolean flagAcceptLeft = false;
	private boolean flagAcceptMid = false;
	private boolean flagAcceptRight = false;

	//// Graphics and shape variables
	int radius; // radius of the pie menu
	int smallRadius; // radius of inner circle
	transient Ellipse2D bigCircle; // the actual pie menu
	transient Ellipse2D smallCircle; // small circle within
	transient Ellipse2D clipCircle; // clipping boundaries
	transient Map newHints; // rendering hints

	//// Pie Menu behavior variables
	boolean flagPenMode;
	double scalingFactor; // how much to scale radius
	//    when rendering text

	//// Pie Menu variables
	String strText; // text name of this menu
	Icon icon; // an icon for this menu
	Container parent; // the component we are on
	TreeModel list; // A treemodel of of menu items
	TreeModel moMarkingMenu; //A treemodel for the marking menu section

	int selected = -1; // # of item selected
	int defaultSelected = -1; // item to select by default
	ShowThread showThread; // thread to show the menu
	//ShowSubmenuThread submenuThread;               // thread to show submenu
	//int submenuPos; // menu position of submenu
	//PieMenu           submenu;                     // the submenu
	//    null if none open
	//PieMenu           parentMenu = null;           // the parent menu
	//   of this submenu
	//   (if it is one)

	//// Listeners
	ArrayList popupMenuListeners; // listeners on the popup

	TreeGraphicModel moGraphicModel;

	//try to record down the menu action starting time
	long mlMenuActionStartTime;
	long mlMenuActionStopTime;

	int startMouseX, startMouseY, stopMouseX, stopMouseY;

	//MousePath will store the movement of mouse after the menu has been triggered
	MousePath moMouseMovement = new MousePath();

	//now, let's worry about where to render the text
	//currently, i use linear interpolation to render the text, but the position is the choosen well
	//i want to be able to adjust the positions so it can work better use a double array
	//this is a temporary hack
	//the first value is not used for Sunburst since level starts from 1, the second one is the inner most 
	//level, and etc ..., but the first is used by Marking menu
	double[] mdRelativeStringPosition =
		{ 0.40, 0.67, 0.75, 0.80, 0.84, 0.88, 0.90, 0.99 };

	private static double TRIGGER_DISTANCE = 25.0;

	public static void debug(String s) {
		if (DEBUG) {
			System.out.println(s);
		}
	}

	//===========================================================================
	//===   POLAR INNER CLASS   =================================================

	/**
	 * Holds a polar coordinate.
	 */
	final class PolarCoordinate {

		//------------------------------------------------------------------

		public double radius;
		public double theta;

		//------------------------------------------------------------------

		public PolarCoordinate(double radius, double theta) {
			this.radius = radius;
			this.theta = theta;
		} // of constructor

		//------------------------------------------------------------------

		public String toString() {
			return ("[r: " + radius + ", theta: " + theta + "]");
		} // of toString

		//------------------------------------------------------------------

	} // of inner class PolarCoordinate

	//===   POLAR INNER CLASS   =================================================
	//===========================================================================

	//Let's try something here, I am going to set up a variable 
	//called SunburstMenuItem which is the last sunburstMenuItem, and based on that
	//I will check the multiple level of strokes selected
	SunburstMenuItem moLastItem;

	//TODO: itemlistener
	/**
		* A mouse listener on one of the top-level panes, allowing us to
		* listen on mouse events and popup the menu when appropriate.
		*/
	final class ItemListener
		implements MouseListener, MouseMotionListener, ComponentListener, Serializable {

		//------------------------------------------------------------------

		int pressedX = 0; // (x,y) of where we pressed the mouse
		int pressedY = 0;
		int distX = 0; // (x,y) farthest dragged from pressed (x,y)
		int distY = 0;

		//------------------------------------------------------------------

		private boolean hasTravelledTooFar() {
			if (distX * distX + distY * distY
				> getSmallRadius() * getSmallRadius()) {
				return (true);
			}
			return (false);
		} // of hasTravelledTooFar

		//------------------------------------------------------------------

		/**
		 * Calculate the farthest we have been since we pressed the mouse
		 * button down. This is done since we don't want to show the pie menu if
		 * the right mouse button was dragged around a lot.
		 */
		private void updateDistances(MouseEvent evt) {
			int dx = Math.abs(pressedX - evt.getX());
			int dy = Math.abs(pressedY - evt.getY());

			if (dx > distX) {
				distX = dx;
			}

			if (dy > distY) {
				distY = dy;
			}
		} // of updateDistances

		//------------------------------------------------------------------

		/**
		 * This is the mouse listener that listens on the attached component
		 * to pop up a pie menu after a period of time.
		 */
		public void mousePressed(MouseEvent evt) {
			clearLastMouseEvent();
			Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();

			//need to put the startMouseX and startMouseY here
			startMouseX = evt.getX();
			startMouseY = evt.getY();

			//// 1. If we hit the popup trigger, show the menu.
			if (doesActivateMenu(evt)) {
				pressedX = evt.getX();
				pressedY = evt.getY();
				/*
								if (defaultBigRadius + pressedX > dim.width) {
									pressedX = dim.width - defaultBigRadius;
								}
								if (pressedX - defaultBigRadius < 0) {
									pressedX = defaultBigRadius;
								}
								if (defaultBigRadius + pressedY > dim.height) {
									pressedY = dim.height - defaultBigRadius;
								} */
				distX = 0;
				distY = 0;
				flagJustOpened = false;

				//// 1.1. Show the menu if we can drag-open the pie menu.
				if (isShowing() == false
					&& (isDragOpen() == true || isTapHoldOpen() == true)) {
					show(evt.getComponent(), evt.getX(), evt.getY());
				}
			}
			//// 2. Otherwise set the pie menu to the close option.
			else {
				getActiveMenu().setSelectedItem(-1);
			}
		} // of mousePressed

		//------------------------------------------------------------------

		/**
		 * Forward the event to the pie menu listener if we are dragging inside
		 * of the circle. This is necessary because all of the mouse events will
		 * still be (correctly) dispatched to here instead of to the PieMenu.
		 */
		public void mouseDragged(MouseEvent evt) {
			setLastMouseEvent(evt);

			//// 1.1. Basically ignore the event if we are invisible. Just update
			////      where we will show the piemenu. Since the pie menu is not
			////      showing, don't add its coordinates to the update location.
			if (isShowing() == false) {
				updateShowLocation(evt.getX(), evt.getY());
				updateDistances(evt);

				moMouseMovement.addPoint(evt.getX(), evt.getY());
				
				double distance =
								G.intPointDistance(
									startMouseX,
									startMouseY,
									evt.getX(),
									evt.getY());
									
				if(distance > TRIGGER_DISTANCE && distance < (TRIGGER_DISTANCE + 100.0)){
					//getGraphics().drawString("1", evt.getX(), evt.getY());
				}

				//// 1.2. Update the distance, since we only want to open the
				////      pie menu in tap-mode if we have stayed near where we
				////      started.
				if (isTapOpen() == true || isTapHoldOpen() == true) {
					if (hasTravelledTooFar() == true) {
						if (showThread != null) {
							showThread.abort();
							showThread = null;
						}
					} else {
						evt.consume();
					}
				}

				//added by Shen, besides this, we need to do a few things:
				//1. I need to record the trajectory

				return;
			}

			//// 2. Find the active submenu and dispatch to it.
			//PieMenuHandler redispatcher;
			//evt          = convertMouseEventSpace(evt);
			//redispatcher = getDispatcher();
			//redispatcher.handleMouseDragged(evt);

		} // of mouseDragged

		//------------------------------------------------------------------

		/**
		 * Forward to the other listener. See documentation above for
		 * mouseDragged().
		 */
		public void mouseReleased(MouseEvent evt) {
			setLastMouseEvent(evt);

			//added by Shen
			//when mouse is released, I need to record down the coordinates
			//and I need to check the distance of the mouse travel
			//if it's too small, I won't do anything, but if it's 
			//a long enough drag, I will think it's a trigger for a marking
			//menu action 
			stopMouseX = evt.getX();
			stopMouseY = evt.getY();
			//TODO: maybe I need to merge pressedX with startMouseX 
			//After calculating the distance, I need to calculate the speed
			//of the mouse movement, well, not really, if the menu is not shown
			//I will regard this as a marking menu action
			double distance =
				G.intPointDistance(
					startMouseX,
					startMouseY,
					stopMouseX,
					stopMouseY);

			//we try to avoid something too little an action to trigger the menu
			if (distance > TRIGGER_DISTANCE) {

				
				int slideNum = _getDetailedSliceNumber(stopMouseX, stopMouseY, startMouseX, startMouseY, getStartRadian(), 2*Math.PI, moGraphicModel.getMarkingMenu().size());
				SunburstMenuItem item = null;
								if(slideNum != -1){
				
								
									item = (SunburstMenuItem) moGraphicModel.getMarkingMenu().get(slideNum);
									
									G.message("Marking Menu item: "+item.getText());
								}
				
				
			}
			//// 1. If we are invisible, see if we have the equivalent of a
			////    mouseClicked() event. The reason I didn't just implement
			////    mouseClicked() is because when people are using pens,
			////    sometimes you drag it when trying to click. That's the
			////    way pens are.
			if (isShowing() == false) {
				if (doesActivateMenu(evt)
					&& (isTapOpen() == true || isTapHoldOpen() == true)) {
					if (isTapOpen() == true && hasTravelledTooFar() == false) {
						clearLastMouseEvent();
						show(evt.getComponent(), evt.getX(), evt.getY());
					} else if (isTapHoldOpen() == true) {
						if (showThread != null) {
							showThread.abort();
							showThread = null;
						}
					}
				}
				return;
			}

			//// 2. We don't want pie menus to close immediately if we are
			////    still holding the button down.
			if (isShowing() == true
				&& isTapHoldOpen() == true
				&& flagJustOpened) {
				evt.consume();
				return;
			}

			//// 3. Find the active submenu and dispatch to it.
			//PieMenuHandler redispatcher;
			//evt           = convertMouseEventSpace(evt);
			//redispatcher  = getDispatcher();
			//redispatcher.handleMouseReleased(evt);
		} // of mouseReleased

		//------------------------------------------------------------------

		public void mouseClicked(MouseEvent evt) {
			setLastMouseEvent(evt);
		} // of mouseClicked

		//------------------------------------------------------------------

		public void mouseMoved(MouseEvent evt) {
			//// 1. Just forward the event to drag.
			mouseDragged(evt);
		} // of mouseMoved

		//------------------------------------------------------------------

		public void mouseEntered(MouseEvent evt) {
		}
		public void mouseExited(MouseEvent evt) {
		}

		//------------------------------------------------------------------

		public void componentHidden(ComponentEvent evt) {
		}
		public void componentMoved(ComponentEvent evt) {
		}
		public void componentShown(ComponentEvent evt) {
		}

		//------------------------------------------------------------------

		/**
		 * Just so layout will not be messed up on resize.
		 */
		public void componentResized(ComponentEvent evt) {
			if (isShowing() == true) {
				hideAll();
			}
		} // of componentResized

	} // of ItemListener

	/**
		* Listens to mouse events, forwarding them to the correct submenu's handler.
		*/
	final class SunburstMenuListener
		implements MouseListener, MouseMotionListener, Serializable {

		//------------------------------------------------------------------

		public final void mouseClicked(MouseEvent evt) {
			//somehow mouse clicked is never executed
			G.debug("Mouse clicked");

		}

		public final void mouseExited(MouseEvent evt) {
		}

		//------------------------------------------------------------------

		public final void mouseEntered(MouseEvent evt) {
			//// 0. Consume the event in case other people check this value. Yum!
			evt.consume();

			//// 1. Since we entered the pie menu, it's okay to forward
			////    events to the pie slice that just had it's submenu closed.
			////
			////    Actually, it's not since you get a mouseEntered() event
			////    if you closed a pie submenu and the cursor happens to
			////    be over the pie menu. Just do nothing.

			//      flagJustClosedSubmenu = false;

		} // of mouseEntered

		//------------------------------------------------------------------

		/**
		 * This is the mouse listener that listens on the pie menu
		 * when it is already popped up.
		 */
		public final void mousePressed(MouseEvent evt) {
			//// 0. Consume the event in case other people check this value. Yum!
			evt.consume();
			G.debug("mouse pressed");
			flagJustOpened = false;

			//// 1. Save the last event for redispatching purposes.
			////    Save here instead of in the mouse listener because
			////    the mouse listener may be bypassed.
			setLastMouseEvent(evt);

			//need to set the selected Item here and try to highlight it
			//setSelectedItem(getSliceNumber(evt.getX(), evt.getY()));
			//basically, I will do the following: 
			//1. determine if it's in the menu mode or not
			//this will be determined if I triggered the menu
			//2. once it's in the menu mode, the following press down
			//will trigger the timer, and the release of the mouse button 
			//will 
			//need to define several variables: such as start time

			if (isShowing()) {
				//start the timer, and do something
				mlMenuActionStartTime = System.currentTimeMillis();
				startMouseX = evt.getX();
				startMouseY = evt.getY();
				moMouseMovement.addPoint(startMouseX, startMouseY);
			} else {

			}

		} // of mousePressed

		//------------------------------------------------------------------

		public final void mouseReleased(MouseEvent evt) {
			//// 0. Consume the event in case other people check this value. Yum!

			evt.consume();

			//// 1. Save the last event for redispatching purposes.
			////    Save here instead of in the mouse listener because
			////    the mouse listener may be bypassed.
			setLastMouseEvent(evt);
			G.debug("mouse released " + evt.toString());

			//// 0.2.
			if (flagJustOpened == true && isTapHoldOpen() == true) {
				flagJustOpened = false;
				return;
			}
			flagJustOpened = false;

			//// 1. See if the event is in us or not. If the event is in the
			////    pie menu, then just proceed normally. If it is not, then
			////    we either activate an item (if it is the popup trigger or
			////    if it was entirely dragged through) or cancel and close
			////    just this pie menu (if it is not the popup trigger).
			if (!doesActivateMenu(evt)
				&& !flagDraggedInPieMenu
				&& !bigCircle.contains(evt.getX(), evt.getY())) {
				//firePopupMenuCanceled();
				G.debug("inside !doesActivateMenu");
				SunburstMenuItem item =
					moGraphicModel.pickItem(
						(double) evt.getX(),
						(double) evt.getY());
				if (item != null) {
					G.message(
						"SunburstMenu: your selection is " + item.getText());
					G.debug("the selected item is " + item.getText());
				}
				setVisible(false);
				return;
			}

			//temporary hack
			setVisible(false);

			//set the stop time for mouse click
			mlMenuActionStopTime = System.currentTimeMillis();
			stopMouseX = evt.getX();
			stopMouseY = evt.getY();

			moMouseMovement.addPoint(stopMouseX, stopMouseY);

			double totalDistance = moMouseMovement.getTotalDistance();
			long duration = (mlMenuActionStopTime - mlMenuActionStartTime);
			double speed = totalDistance / duration;

			G.debug("total distance is " + totalDistance);
			G.debug("duration is " + duration);
			G.debug("Speed is " + speed);
			
			double distance =
							G.intPointDistance(
								startMouseX,
								startMouseY,
								stopMouseX,
								stopMouseY);
			//we will consider that if the speed is less than some number,
			//the user wants to use it as marking menu
			//otherwise, it's a sunburst, and user can click and drag to and stop at that place

			if (speed > 0.3 & distance > TRIGGER_DISTANCE) {
				
			
				
				int slideNum = getMarkingSliceNumber(stopMouseX, stopMouseY);

				if (slideNum != -1) {

					setSelectedItem(slideNum);
					G.debug("slide number is " + slideNum);
					SunburstMenuItem item =
						(SunburstMenuItem) moGraphicModel.getMarkingMenu().get(
							slideNum);
					G.message(
						"MarkingMenu: your selection is " + item.getText());
				}
			} else {
				G.debug("we are in Sunburst Menu mode");

			}
			//reset the mouse movement store
			moMouseMovement.reset();

			//// 2. Otherwise activate the menu item selected.
			//int pos = getSliceNumber(evt.getX(), evt.getY());

			//// 3.1. Close the menu if we are in the small circle.
			//if (pos < 0) {
			//firePopupMenuCanceled();
			//hideDescendants();
			//return;
			//}
			//// 3.2. See if we are going to open a popup menu or not.
			////      Either case, always mark this as a submenu that cannot
			////      be aborted.
			//maybeShowPieSubmenu(evt.getX(), evt.getY());
			//flagCanAbortSubmenu = false;

			//// 3. Showing a submenu, cannot activate a command, no need
			////    to continue.

			//// 3.3. If not, activate the selected pie menu item.
			// System.out.println("Activate menu item " + pos);

			//// 4.1. See if the item is enabled or not.

		} // of mouseReleased

		//------------------------------------------------------------------

		public final void mouseDragged(MouseEvent evt) {
			//// 0. Consume the event in case other people check this value. Yum!
			evt.consume();

			//// 1. Save the last event for redispatching purposes.
			////    Save here instead of in the mouse listener because
			////    the mouse listener may be bypassed.
			setLastMouseEvent(evt);

			if (isShowing()) {
				//0.0 try to do the expand target here, and see if it works or not
				//expandNodes(evt.getX(),evt.getY());

				//// 0. See if the small circle contains it.
				if (!smallCircle.contains(evt.getX(), evt.getY())) {
					int sliceNum =
						getMarkingSliceNumber(evt.getX(), evt.getY());
					setSelectedItem(sliceNum);
					repaintBounds();
				} //repaintBounds();
				moMouseMovement.addPoint(evt.getX(), evt.getY());
				//G.debug("slice number is "+ sliceNum);
			} else {
				//G.debug("inside of mouse move not showing x is "+ evt.getX() + ", y is "+evt.getY());
			}

		} // of mouseDragged

		//------------------------------------------------------------------

		public final void mouseMoved(MouseEvent evt) {
			//// 0. Consume the event in case other people check this value. Yum!
			evt.consume();

			//// 1. Save the last event for redispatching purposes.
			////    Save here instead of in the mouse listener because
			////    the mouse listener may be bypassed.
			setLastMouseEvent(evt);
			long time = System.currentTimeMillis();
			if (isShowing()) {

				moGraphicModel.resetAllMenuItems();
				if (!moGraphicModel
					.getMarkingCircle()
					.contains(evt.getX(), evt.getY())) {

					SunburstMenuItem item =
						moGraphicModel.pickItem(
							(double) evt.getX(),
							(double) evt.getY());
					if (item != null)
						item.setHighlight(true);
					repaintBounds();
				} else {
					//reset everything so we don't show the highlight here
					moGraphicModel.resetAllMenuItems();
					repaintBounds();
				}
			}
			//G.debug("x is "+ evt.getX() + ", y is "+ evt.getY());

			//I need to record all the points can be taken from here
			//these points will be used to calculate the trajectry and 
			//I will know the distance it really traveled 

		} // of mouseMoved

	} // of inner class PieMenuListener

	//TODO: this is a bookmark for Constructor

	//try to use this one for now, don't use the 
	//other ones because they can't show the menu correctly
	//the reason is unknown right now, but this one works
	public SunburstMenuNoExpansionRotate(ArrayList treeModels, TreeModel markingMenu) {
		this(treeModels, markingMenu, "", null, DEFAULT_BIG_RADIUS);
	}

	//-----------------------------------------------------------------

	//-----------------------------------------------------------------

	/**
	 * Create a Sunburst menu with the specified parameters.
	 *
	 * @param str    is the name of this pie menu.
	 * @param icon   is the icon for this pie menu.
	 * @param radius is the intiial radius of the pie menu.
	 */
	public SunburstMenuNoExpansionRotate(
		ArrayList treeModels,
		TreeModel markingMenu,
		String str,
		Icon icon,
		int radius) {

		//// 0.1. Initialize to defaults.
		setFillColor(getDefaultFillColor());
		setLineColor(getDefaultLineColor());
		setFontColor(getDefaultFontColor());
		setSelectedColor(getDefaultSelectedColor());
		setFont(getDefaultFont());
		setLineWidth(getDefaultLineWidth());
		//setLineNorth(getDefaultLineNorth());
		setScalingFactor(getDefaultScalingFactor());

		//submenuIconImage     = getDefaultSubmenuIcon();

		//// 0.2. Initialize some behaviors.
		bigCircle = new Ellipse2D.Double();
		smallCircle = new Ellipse2D.Double();
		clipCircle = new Ellipse2D.Double();
		list = new TreeModel();
		//timer                = new BlinkTimer();

		//we allow multiple treemodels to be added to the system
		//and they will be merged into a list by the following method
		if (treeModels != null && treeModels.size() != 0) {
			for (int i = 0; i < treeModels.size(); i++) {
				addItem((TreeModel) treeModels.get(i));
			}
		}

		moMarkingMenu = markingMenu;
		//but we only allow for one marking menu treemodel to be added here
		//this one contains the default menu items to be triggered 
		moGraphicModel = new TreeGraphicModel(list, markingMenu);

		setDoubleBuffered(true);

		//// 0.3. Initialize the listeners.
		popupMenuListeners = new ArrayList();

		//// 0.4. Only draw what we say to draw, don't draw anything else.
		////      If this is true, then we get some strange repaint problems.
		setOpaque(false);

		//// 1. Setup the listener that we will attach to the parent,
		////    and the one we will attach to ourself.
		//handler   = new PieMenuHandler();
		lstnr = new SunburstMenuListener();
		clistener = new ItemListener();

		//// 2. Setup Component stuff, our location, visibility, and font.
		setLocation(0, 0);
		//setVisible(true);
		setFont(getDefaultFont());

		//// 3. Set the radius, string, and icon.
		setBigRadius(getDefaultBigRadius());
		setSmallRadius(getDefaultSmallRadius());
		setMenuSize(moGraphicModel.getMaxDepth());
		//setIcon(icon);
		//setText(str);

		//// 4. And now setup a listener on ourself.
		addMouseListener(lstnr);
		addMouseMotionListener(lstnr);

		//// 5. Setup the rendering hints.
		newHints = new HashMap();
		newHints.put(
			RenderingHints.KEY_ANTIALIASING,
			RenderingHints.VALUE_ANTIALIAS_ON);
		newHints.put(
			RenderingHints.KEY_COLOR_RENDERING,
			RenderingHints.VALUE_COLOR_RENDER_QUALITY);
		newHints.put(
			RenderingHints.KEY_RENDERING,
			RenderingHints.VALUE_RENDER_QUALITY);
		newHints.put(
			RenderingHints.KEY_TEXT_ANTIALIASING,
			RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

	} // of default constructor

	//TODO: end of constructor

	/**
		  * Given a coordinate, get the corresponding slice number of the pie.
		  *
		  * @return the slice number, zero-based, starting at north, going
		  *         counter-clockwise. Returns -1 on error or if the small circle
		  *         contains it.
		  */
	private int getMarkingSliceNumber(int x, int y) {
		int maxDepth = moGraphicModel.getMaxDepth();
		int centerX = (maxDepth * radius + 2 * radius) / 2;
		int centerY = centerX;
		return _getDetailedSliceNumber(
			x,
			y,
			centerX,
			centerY,
			getStartRadian(),
			2 * Math.PI,
			moGraphicModel.getMarkingMenu().size());
	} // of method

	/**
			 * Given a coordinate, get the corresponding slice number of the pie.
			  *
			  * @return the slice number, zero-based, starting at north, going
			  *         counter-clockwise. Returns -1 on error or if the small circle
			  *         contains it.
			  */
	private int getDetailedSliceNumber(
		int x,
		int y,
		int centerX,
		int centerY,
		SunburstMenuItem item) {

		if (item == null)
			return _getDetailedSliceNumber(
				x,
				y,
				centerX,
				centerY,
				getStartRadian(),
				2 * Math.PI,
				moGraphicModel.getLayer(0).size());
		else {
			//TODO: I need to figure this part out: how to determine the radian stuff
			double radian =
				Math.toRadians(
					item.getArc().getAngleStart()
						+ item.getArc().getAngleExtent() / 2.0)
					- Math.PI / 2.0;
			G.debug("item.getArc is " + item.getArc().getAngleStart());
			G.debug("middle anlge is " + (item.getArc().getAngleStart() + item.getArc().getAngleExtent()/2.0));
			G.debug("Radian to degree is " + Math.
			toDegrees(radian));
			return _getDetailedSliceNumber(
				x,
				y,
				centerX,
				centerY,
				radian,
				Math.PI,
				item.getChildCount());
		}
	} // of method

	/**
	  * Given a coordinate, get the corresponding slice number of the pie.
	  *
	  * @return the slice number, zero-based, starting at north, going
	  *         counter-clockwise. Returns -1 on error or if the small circle
	  *         contains it.
	  */
	/*private int _getMarkingSliceNumber(int x, int y, int centerX, int centerY) {
		PolarCoordinate polar = _getPolarCoordinates(x, y, centerX, centerY);
		int numItems = moGraphicModel.getMarkingMenu().size();
	
		//// 0. See if the small circle contains it.
	
	
		//// 1. Initialize the radian variables.
		double currentRadian = getStartRadian();
		double stepRadian = 2 * Math.PI / numItems;
		double theta = 2 * Math.PI + polar.theta;
	
		//// 2. Figure out which segment we are in.
		int count = 0;
		for (int i = 0; i < 2 * numItems; i++) {
			if ((currentRadian < theta)
				&& (theta <= currentRadian + stepRadian)) {
				return (count % numItems);
			}
			count++;
			theta -= stepRadian;
		}
	
		//// 3. Return -1 for error.
		return (-1);
	} // of method
	*/

	/**
		  * Given coordinates, starting angle, end angle, and how many divisions
		  * it has, try to get the slice number
		  *
		  * @return the slice number, zero-based, starting at north, going
		  *         counter-clockwise. Returns -1 on error or if the small circle
		  *         contains it.
		  */
	private int _getDetailedSliceNumber(
		int x,
		int y,
		int centerX,
		int centerY,
		double startRadian,
		double angleExtent,
		int numItems) {
		PolarCoordinate polar = _getPolarCoordinates(x, y, centerX, centerY);

		//// 0. See if the small circle contains it.
		/*if (smallCircle.contains(x, y)) {
		   return (-1);
		}*/

		//// 1. Initialize the radian variables.
		double currentRadian = startRadian;

		//we only use 180 degree here because the rest of the 180 degree, we 
		//use it as undo or back to the parent level
		double stepRadian = angleExtent / numItems;
		double theta = 2 * Math.PI + polar.theta;

		//// 2. Figure out which segment we are in.
		int count = 0;
		for (int i = 0; i < 2 * numItems; i++) {
			if ((currentRadian < theta)
				&& (theta <= currentRadian + stepRadian)) {
				return (count % numItems);
			}
			count++;
			theta -= stepRadian;
		}

		//// 3. Return -1 for error.
		//over here, error basically means backtrack, but do we need to have
		//a code for error as well? 
		return (-1);
	} // of method

	//===========================================================================
	//===   POLAR METHODS   =====================================================

	/**
	 * Given an (x,y) coordinate, figure out the angle from the center of this
	 * pie menu, assuming that the origin is at (radius, radius). We can assume
	 * the origin is there since the coordinate space has (0, 0) at the top-left
	 * of this pie menu, meaning that (radius, radius) is the center of this
	 * pie menu.
	 *
	 * <PRE>
	 *                     0 radians
	 *         ---------    |
	 *         |            |
	 *        \/            |
	 *          ------------|--------------
	 *                      |
	 *                      |
	 *                      |
	 *                    pi radians
	 * </PRE>
	 *
	 * @return the angle (in radians) from the positive y-axis,
	 *         going counter-clockwise. Ranges from 0 to less than 2*PI.
	 */
	private PolarCoordinate _getPolarCoordinates(
		int x,
		int y,
		int centerX,
		int centerY) {
		int xx = x - centerX;
		int yy = y - centerY;

		//// 2. Figure out the radius. Just the distance from (0, 0).
		double distance = Math.sqrt(xx * xx + yy * yy);

		//// 3. Now figure out the angle.
		double normalizedRadius = ((double) xx) / distance;
		double theta = Math.acos(normalizedRadius);

		//// 3.1. Have to dress up theta a little. The function acos() only
		////      returns values from 0.0 to PI. This should mean that
		////      acos() returns the correct value if y is positive.
		////      Normally, this would be < and not >, but screen coordinate
		////      system is backwards.
		if (yy > 0) {
			theta = 2 * Math.PI - theta;
		}

		//// 4. Okay, that's it. Return the answer.
		return (new PolarCoordinate(distance, theta));
	} // of method

	/**
	 * 	This one will return the polar coordinates according to the center
	 * provided
		 * Given an (x,y) coordinate, figure out the angle from the center of this
		 * pie menu, assuming that the origin is at (radius, radius). We can assume
		 * the origin is there since the coordinate space has (0, 0) at the top-left
		 * of this pie menu, meaning that (radius, radius) is the center of this
		 * pie menu.
		 *
		 * <PRE>
		 *                     0 radians
		 *         ---------    |
		 *         |            |
		 *        \/            |
		 *          ------------|--------------
		 *                      |
		 *                      |
		 *                      |
		 *                    pi radians
		 * </PRE>
		 *
		 * @return the angle (in radians) from the positive y-axis,
		 *         going counter-clockwise. Ranges from 0 to less than 2*PI.
		 */
	private PolarCoordinate getPolarCoordinates(int x, int y) {
		// 1. First translate such that x and y are relative to (0, 0).
		int maxDepth = moGraphicModel.getMaxDepth();
		int centerX = (maxDepth * radius + 2 * radius) / 2;
		int centerY = centerX;
		return _getPolarCoordinates(x, y, centerX, centerY);
	} // of method

	/**
	   * Hide the pie menu, all of its submenus, and all of its parent menus.
	   */
	private void hideAll() {
		this.setVisible(false);
	}

	/**
	   * Update either where a piemenu or one of its submenu will be displayed.
	   * Technically, these should be two methods, but only one of them will have
	   * an effect at a time, so it should be okay.
	   *
	   * @param x is the absolute coordinate to show the submenu at.
	   * @param y is the absolute coordinate to show the submenu at.
	   */
	void updateShowLocation(int x, int y) {

		//// 2. If we are going to show a piemenu, and we are in
		////    tap mode, then update where we will show it.
		if (showThread != null && (isTapOpen() || isTapHoldOpen())) {
			showThread.setShowLocation(x, y);
		}
	} // of method

	/**
		* Clear out the last mouse event we have.
		*/
	void clearLastMouseEvent() {
		lastEvent = null;
	} // of method

	/**
		* Set what the latest interesting mouse event we have is.
		*/
	void setLastMouseEvent(MouseEvent evt) {
		lastEvent =
			new MouseEvent(
				evt.getComponent(),
				evt.getID(),
				0,
				evt.getModifiers(),
				evt.getX(),
				evt.getY(),
				evt.getClickCount(),
				evt.isPopupTrigger());
	} // of method

	/**
		  * Get the deepest-level pie menu that is showing. This is necessary for
		  * dispatching events to the correct pie menu in the correct coordinate
		  * system.
		  */
	SunburstMenuNoExpansionRotate getActiveMenu() {
		SunburstMenuNoExpansionRotate pm = getActiveMenuHelper();
		if (pm == null) {
			return (this);
		} else {
			return (pm);
		}
	} // of method

	//-----------------------------------------------------------------

	SunburstMenuNoExpansionRotate getActiveMenuHelper() {
		//// 1.1. Recurse to the deepest-level pie menu.

		//// 1.2. Once we are at the deepest level, return the first
		////      one that is showing.
		if (isShowing() == true) {
			return (this);
		} else {
			return (null);
		}
	} // of method

	/**
		* Get the menu item that is currently selected.
		*
		* @return the index of the currently selected item (0 based),
		*         or -1 if the small center circle is selected.
		*/
	public int getSelectedItem() {
		return (selected);
	} // of method

	/**
		 * Check if the sequence {button-down, button-up} opens up the pie menu.
		 */
	public static boolean isTapOpen() {
		return (defaultOpenState == STATE_TAP_OPEN);
	} // of method

	/**
	 * Check if the sequence {button-down, hold without moving} opens up the
	 * pie menu.
	 */
	public static boolean isTapHoldOpen() {
		return (defaultOpenState == STATE_TAPHOLD_OPEN);
	} // of method

	/**
	 * Check if the sequence {button-down} opens up the pie menu.
	 */
	public static boolean isDragOpen() {
		return (defaultOpenState == STATE_DRAG_OPEN);
	} // of method

	/**
		 * Specify what triggers the popup of this pie menu.
		 * Checks in this order: left, right, middle
		 */
	public boolean doesActivateMenu(MouseEvent evt) {
		if (flagAcceptLeft) {
			return (SwingUtilities.isLeftMouseButton(evt));
		} else if (flagAcceptRight) {
			return (SwingUtilities.isRightMouseButton(evt));
		} else if (flagAcceptMid) {
			return (SwingUtilities.isMiddleMouseButton(evt));
		} else {
			return (SwingUtilities.isRightMouseButton(evt));
		}
	} // of method

	//-----------------------------------------------------------------

	/**
	 * A convenience method to have the pie menu listen on the specified
	 * component. By default, this makes the pie menu listen for right mouse
	 * clicks. If any are received, then the pie menu will pop open.
	 *
	 * @param c is the component to attach the pie menu to. Right now,
	 *        PieMenu only works with Java Swing components.
	 */
	public void addSunburstMenuTo(Component c) {
		c.addMouseListener(clistener);
		c.addMouseMotionListener(clistener);
		c.addComponentListener(clistener);
	} // of method

	/**
	   * Set which menu item is currently selected.
	   *
	   * @param index is the index of the selected item. It has a value less than
	   *              0 to select the center circle.
	   */
	public void setSelectedItem(int index) {
		// System.out.println("   setSelectedItem");
		// System.out.println("      old " + selected);
		// System.out.println("      new " + index);

		//// 0. See if select has been turned on or off.
		if (selectIsEnabled() == false) {
			return;
		}

		//// 1. No point in doing anything unless we have something new selected.
		if (index != selected) {

			//// 1.3. Set the selected item.
			this.selected = index;

			//// 1.4. Okay to forward events to the former pie submenu slice again.
			//flagJustClosedSubmenu = false;

			//// 1.5. Repaint the PieMenu.
			repaintBounds();
		}

	} // of method

	/**
		 * Calls repaint on the correct bounds of the Pie Menu.
		 */
	private void repaintBounds() {
		//// 1. No point in repainting if no parent.
		if (parent == null) {
			return;
		}

		//// 2. Repaint the bounds.
		Point pt = getLocation();
		Rectangle rect = getBounds();
		parent.repaint(rect.x, rect.y, rect.width, rect.height);
	} // of method

	//-----------------------------------------------------------------

	private static synchronized boolean selectIsEnabled() {
		return (enableSelect);
	} // of method

	/**
		* Set the color of the pie menu items.
		*/
	public void setFillColor(Color newColor) {
		fillColor = newColor;
	} // of method

	//-----------------------------------------------------------------

	/**
		* Set the color lines are rendered in.
		*/
	public void setLineColor(Color newColor) {
		lineColor = newColor;
	} // of method

	//-----------------------------------------------------------------

	/**
		* Set the color of the selected item.
		*/
	public void setSelectedColor(Color newColor) {
		selectedColor = newColor;
		defaultSelectedItemColor =
			new Color(
				selectedColor.getRed(),
				selectedColor.getGreen(),
				selectedColor.getBlue(),
				127);
	} // of method

	//-----------------------------------------------------------------

	/**
		* Set the color text is rendered in.
		*/
	public void setFontColor(Color newColor) {
		fontColor = newColor;
	} // of method

	/**
		* Get the color text is rendered in.
		*/
	public Color getFontColor() {
		return (fontColor);
	} // of method

	//-----------------------------------------------------------------

	/**
		* Set the font the pie menu will use.
		*/
	public void setFont(Font newFont) {
		super.setFont(newFont);
	} // of method

	/**
		* Get the font the pie menu will use.
		*/
	public Font getFont() {
		return (super.getFont());
	} // of method

	//-----------------------------------------------------------------

	/**
		* Set the line width for the pie menu.
		*/
	public void setLineWidth(float newWidth) {
		stroke = new BasicStroke(newWidth);
	} // of method
	/**
		* Get the radius for the pie menu.
		*/
	public int getBigRadius() {
		return (radius);
	} // of method

	//-----------------------------------------------------------------

	/**
		* Set the radius of the inner circle for the pie menu.
		*/
	public void setSmallRadius(int newRadius) {
		this.smallRadius = newRadius;
		updateShape();
	} // of method

	/**
		* Get the radius of the inner circle for the pie menu.
		*/
	public int getSmallRadius() {
		return (smallRadius);
	} // of method

	//-----------------------------------------------------------------

	/**
		* Set the scaling factor for the pie menu.
		*/
	public void setScalingFactor(double newScalingFactor) {
		scalingFactor = newScalingFactor;
	} // of method

	/**
		* Get the scaling factor for the pie menu.
		*/
	public double getScalingFactor() {
		return (scalingFactor);
	} // of method

	//-----------------------------------------------------------------

	/**
		* Set whether we draw a line to north, or draw the first slice such
		* that it is centered on north.
		*
		* @param flag is true if a line is to be drawn to north, false otherwise.
		*/
	public void setLineNorth(boolean flag) {
		flagLineNorth = flag;
	} // of method

	/**
	  * Set the default fill color for all new pie menus created
	  * after this value is set.
	  */
	public static void setDefaultFillColor(Color newColor) {
		defaultFillColor = newColor;
	} // of method

	/**
	 * Get the default fill color for all new pie menus created.
	 */
	public static Color getDefaultFillColor() {
		return (defaultFillColor);
	} // of method

	//-----------------------------------------------------------------

	/**
	 * Set the default line color for all new pie menus created
	 * after this value is set.
	 */
	public static void setDefaultLineColor(Color newColor) {
		defaultLineColor = newColor;
	} // of method

	/**
	 * Get the default line color for all new pie menus created.
	 */
	public static Color getDefaultLineColor() {
		return (defaultLineColor);
	} // of method

	//-----------------------------------------------------------------

	/**
	 * Set the default selected color for all new pie menus created
	 * after this value is set.
	 */
	public static void setDefaultSelectedColor(Color newColor) {
		defaultSelectedColor = newColor;
	} // of method

	/**
	 * Get the default selected color for all new pie menus created.
	 */
	public static Color getDefaultSelectedColor() {
		return (defaultSelectedColor);
	} // of method

	//-----------------------------------------------------------------

	/**
	 * Set the default font color for all new pie menus created
	 * after this value is set.
	 */
	public static void setDefaultFontColor(Color newColor) {
		defaultFontColor = newColor;
	} // of method

	/**
	 * Get the default font color for all new pie menus created.
	 */
	public static Color getDefaultFontColor() {
		return (defaultFontColor);
	} // of method

	//-----------------------------------------------------------------

	/**
	 * Set the default font for all new pie menus created
	 * after this value is set.
	 */
	public static void setDefaultFont(Font newFont) {
		defaultFont = newFont;
	} // of method

	/**
	 * Get the default font for all new pie menus created.
	 */
	public static Font getDefaultFont() {
		return (defaultFont);
	} // of method

	//-----------------------------------------------------------------

	/**
	 * Set the default line width for all new pie menus created
	 * after this value is set.
	 */
	public static void setDefaultLineWidth(float newLineWidth) {
		defaultLineWidth = newLineWidth;
	} // of method

	/**
	 * Get the default line width for all new pie menus created.
	 */
	public static float getDefaultLineWidth() {
		return (defaultLineWidth);
	} // of method

	//-----------------------------------------------------------------

	/**
	 * Set the default radius for all new pie menus created
	 * after this value is set.
	 */
	public static void setDefaultBigRadius(int newRadius) {
		defaultBigRadius = newRadius;
	} // of method

	//-----------------------------------------------------------------

	/**
	 * Set the default radius of the inner circle for all new pie menus created
	 * after this value is set.
	 */
	public static void setDefaultSmallRadius(int newRadius) {
		defaultSmallRadius = newRadius;
	} // of method

	//-----------------------------------------------------------------

	/**
	 * Set the default scaling factor for all new pie menus created
	 * after this value is set.
	 */
	public static void setDefaultScalingFactor(double newScalingFactor) {
		defaultScalingFactor = newScalingFactor;
	} // of method

	/**
	 * Get the default scaling factor for all new pie menus created.
	 */
	public static double getDefaultScalingFactor() {
		return (defaultScalingFactor);
	} // of method

	//-----------------------------------------------------------------

	/**
		* Get whether we draw a line to north, or draw the first slice such
		* that it is centered on north.
		*/
	public boolean getLineNorth() {
		return (flagLineNorth);
	} // of method

	/**
	   * Get the color of the pie menu items.
	   */
	public Color getFillColor() {
		return (fillColor);
	} // of method

	/**
		* Get the color of the selected item.
		*/
	public Color getSelectedColor() {
		return (selectedColor);
	} // of method

	/**
	   * Get the color lines are rendered in.
	   */
	public Color getLineColor() {
		return (lineColor);
	} // of method

	/**
		* Get the default radius for all new pie menus created.
		*/
	public static int getDefaultBigRadius() {
		return (defaultBigRadius);
	} // of method

	/**
	   * Get the default radius of the inner circle for all new pie menus created.
	   */
	public static int getDefaultSmallRadius() {
		return (defaultSmallRadius);
	} // of method

	/**
		* Get the radian of the first position. We always start at north.
		*/
	private double getStartRadian() {
		int numItems = getItemCount();

		if (getLineNorth() == true || numItems <= 1) {
			return (DEFAULT_START);
		} else {
			double offset = 2 * Math.PI / numItems;
			return (DEFAULT_START - offset / 2);
		}
	} // of method

	/**
	   * Set the default appearance delay for the initial pie menu.
	   * This is a global behavior for all pie menus.
	   */
	public static void setAllInitialDelay(long newDelay) {
		defaultInitialDelay = newDelay;
	} // of method

	/**
	 * Get the default appearance delay for the initial pie menu.
	 */
	public static long getAllInitialDelay() {
		return (defaultInitialDelay);
	} // of method

	/**
	   * Set whether we can auto-open pie menus or not.
	   */
	public static void setAllAutoOpen(boolean flag) {
		defaultAutoOpen = flag;
	} // of method

	/**
	 * Check whether we can auto-open pie menus or not.
	 */
	public static boolean getAllAutoOpen() {
		return (defaultAutoOpen);
	} // of method

	//-----------------------------------------------------------------

	/**
		* Switch the individual pie menu so that it is in the correct mode (pen or
		* mouse) if it is not.
		*/
	private static void updatePieMenuToCurrentMode(SunburstMenuNoExpansionRotate pm) {
		updateToPenMode(pm);
		updateToMouseMode(pm);
	} // of method

	//-----------------------------------------------------------------

	/**
		* Convert an individual pie menu to pen mode.
		*/
	private static void updateToPenMode(SunburstMenuNoExpansionRotate pm) {
		if (isAllPenMode() == true && pm.getPenMode() == false) {
			pm.setPenMode();
			setAllTapOpen();
		}
	} // of method

	/**
		* Convert an individual pie menu to mouse mode.
		*/
	private static void updateToMouseMode(SunburstMenuNoExpansionRotate pm) {
		if (isAllMouseMode() == true && pm.getMouseMode() == false) {
			pm.setMouseMode();
			setAllDragOpen();
		}
	} // of method

	//-----------------------------------------------------------------

	/**
		* Set all of the pie menus to be used with pen mode.
		* This is a global behavior for all pie menus.
		*/
	public static void setAllToPenMode() {
		setAllTapOpen();
		defaultFlagPenMode = true;
		//setAllSubmenuDelay((int) (1.5 * DEFAULT_SUBMENU_DELAY));
	} // of method

	/**
		* Set all of the pie menus to be used with mouse mode.
		* This is a global behavior for all pie menus.
		*/
	public static void setAllToMouseMode() {
		setAllDragOpen();
		defaultFlagPenMode = false;
		//setAllSubmenuDelay(DEFAULT_SUBMENU_DELAY);
	} // of method

	//-----------------------------------------------------------------

	/**
		* See if all of the pie menus are in pen mode.
		*/
	public static boolean isAllPenMode() {
		return (defaultFlagPenMode);
	} // of method

	/**
		* See if all of the pie menus are in mouse mode.
		*/
	public static boolean isAllMouseMode() {
		return (!defaultFlagPenMode);
	} // of method

	//-----------------------------------------------------------------

	private void setPenMode() {
		flagPenMode = true;
	} // of method

	private boolean getPenMode() {
		return (flagPenMode);
	} // of method

	//-----------------------------------------------------------------

	private void setMouseMode() {
		flagPenMode = false;
	} // of method

	private boolean getMouseMode() {
		return (!flagPenMode);
	} // of method

	//-----------------------------------------------------------------

	/**
	 * Set that the sequence {button-down, button-up} opens up the pie menu.
	 */
	public static void setAllTapOpen() {
		defaultOpenState = STATE_TAP_OPEN;
	} // of method

	/**
	 * Set that the sequence {button-down, hold without moving} opens up the
	 * pie menu.
	 */
	public static void setAllTapHoldOpen() {
		defaultOpenState = STATE_TAPHOLD_OPEN;
		setAllInitialDelay(2 * DEFAULT_INITIAL_DELAY);
	} // of method

	/**
	 * Set that the sequence {button-down} opens up the pie menu.
	 */
	public static void setAllDragOpen() {
		defaultOpenState = STATE_DRAG_OPEN;
	} // of method

	//-----------------------------------------------------------------

	/**
		* Set the radius for the pie menu.
		*
		* @param radius is the radius of the pie menu from the center.
		*/
	public void setBigRadius(int radius) {
		if (radius > 0) {
			//// 1. Setup the radius. Actually set our size to be slightly
			////    larger than the radius.
			this.radius = radius;

			//setMenuSize(3);
			//// 3. Now update the bigCircle shape.
			updateShape();
		}
	} // of method

	/**
	 * set the size of the menu here, and 
	 * it will be called either in initialization
	 * or after the menu is created, and if the menu
	 * is modified, this method needs to be called again
	 *
	 */
	public void setMenuSize(int noOfRings) {
		//TODO: work on the dimension later

		//remember, the size of the sunburst is noOfRings * radius + 2 radius
		//for the small circle inside
		int sizeX = noOfRings * radius + 2 * radius;
		int sizeY = sizeX;
		setSize(sizeX + 1, sizeY + 1);

		//// 2. Update the min, max, and preferred sizes.
		Dimension dim = new Dimension(sizeX, sizeY);
		setMinimumSize(dim);
		setMaximumSize(dim);
		setPreferredSize(dim);

		updateShape();

	}

	/**
	   * Update the shape of this PieMenu whenever the location or radius is
	   * changed.
	   */
	protected void updateShape() {
		//TODO: need to work on this updateShape
		//// 1. Since our top-left corner is (0, 0), the shape is simply a
		////    circle from (0, 0) to the width and height, both of which
		////    should be 2*radius.
		/*Rectangle bounds = getBounds();
		bigCircle.setFrame(
			0 + radius / 2,
			0 + radius / 2,
			2 * radius,
			2 * radius);
		clipCircle.setFrame(-2, -2, 2 * radius + 4, 2 * radius + 4);
		smallCircle.setFrame(
			radius - smallRadius + radius / 2,
			radius - smallRadius + radius / 2,
			2 * smallRadius,
			2 * smallRadius);*/
	} // of method

	public int getItemCount() {
		return (list.childCount());
	} // of method

	public void addItem(TreeModel item) {
		list.addChild(item);
		//return item;
	}

	//===========================================================================
	//===   DELAYTHREAD INNER CLASS   ===========================================

	/**
	 * A thread that waits around for a while before executing an action.
	 * Didn't use invokeAndWait() or invokeLater() because I wanted a mechanism
	 * for aborting too.
	 */
	abstract class DelayThread extends Thread {

		long ms; // how long to sleep before doing something
		boolean flagContinue = true; // do the command or not?
		boolean flagDone = false; // done or not?

		//------------------------------------------------------------------

		public DelayThread(long ms) {
			this.ms = ms;
			setPriority(Thread.MIN_PRIORITY);
		} // of constructor

		//------------------------------------------------------------------

		/**
		 * Don't execute the command.
		 */
		public void abort() {
			flagContinue = false;
		} // of abort

		//------------------------------------------------------------------

		public boolean isDone() {
			return (flagDone);
		} // of isDone

		//------------------------------------------------------------------

		public final void spin(long ms) {
			//// 1. Sleep for a short while.
			try {
				yield();
				sleep(ms);
				yield();
			} catch (Exception e) {
				//// ignore
			}
		} // of sleep

		//------------------------------------------------------------------

		public final void run() {
			//// 1. Sleep for a short while.
			spin(ms);

			//// 2. Now do something interesting.
			if (flagContinue == true) {
				doit();
			}
			flagDone = true;
			if (flagContinue == false) {
				undo();
			}
		} // of run

		//------------------------------------------------------------------

		/**
		 * Override this method to do something interesting.
		 */
		abstract public void doit();

		//------------------------------------------------------------------

		public void undo() {
		} // of undo

		//------------------------------------------------------------------

	} // of DelayThread

	/**
	   * A thread that displays a pie menu or submenu after a delay.
	   */
	final class ShowThread extends DelayThread {

		Component c; // component to display pie menu in
		int x; // x-coordinate in c's space to display in
		int y; // y-coordinate in c's space to display in

		public ShowThread(Component c, int x, int y) {
			this(c, x, y, getAllInitialDelay());
		} // of constructor

		//------------------------------------------------------------------

		public ShowThread(Component c, int x, int y, long ms) {
			super(ms);
			this.c = c;
			this.x = x;
			this.y = y;
		} // of constructor

		//------------------------------------------------------------------

		/**
		 * Update where the pie submenu will be displayed.
		 */
		public void setShowLocation(int x, int y) {
			this.x = x;
			this.y = y;
		} // of setShowLocation

		//------------------------------------------------------------------

		public void doit() {
			//let's try to not use the blink timer here first
			//I don't know what does it do
			//while (!timer.isDone()) {
			//   spin(50);
			//}

			//// 1. First show the Sunburst menu.
			showInternal(c, x, y);

			//I am not sure I need to implement this one or not
			//let's wait here
			//// 2. Forward the last event to the Sunburst menu. This lets us do
			////    actions on the Sunburst menu before it actually appears on screen.
			//forwardLastEvent();
		} // of doit

		//------------------------------------------------------------------

	} // of inner class ShowThread

	/**
	  * Allows us to turn selection on and off.
	  */
	private static synchronized void enableSelect() {
		enableSelect = true;
	} // of method

	private static synchronized void disableSelect() {
		enableSelect = false;
	} // of method

	/**
	  * Show the Sunburst menu.
	  *
	  * @param invoker is the Component to show the pie menu in.
	  * @param x       is the x-coordinate in the invoker's coordinate space.
	  * @param y       is the y-coordinate in the invoker's coordinate space.
	  * @param ms      is the delay (msec) before showing the pie menu.
	  */
	public void show(Component invoker, int x, int y) {
		//// 1. Just in case we have another show, abort it.
		if (showThread != null) {
			showThread.abort();
		}

		//// 2. Now start the delayed display.
		showThread = new ShowThread(invoker, x, y);
		showThread.start();
	} // of method

	//-----------------------------------------------------------------

	public void showNow(Component invoker, int x, int y) {
		//// 1. Just in case we have another show, abort it.
		if (showThread != null) {
			showThread.abort();
		}

		showInternal(invoker, x, y);
	} // of method

	/**
	   * Show the Sunburst menu.
	   *
	   * @param invoker is the Component to show the pie menu in.
	   * @param x       is the x-coordinate in the invoker's coordinate space.
	   * @param y       is the y-coordinate in the invoker's coordinate space.
	   */
	private void showInternal(Component invoker, int x, int y) {
		debug("invoker x is " + x + " invoke y is " + y);
		enableSelect();

		//The following line is for pen mode or mouse mode, and I don't want to 
		//worry about it now
		//updatePieMenuToCurrentMode(this);

		//// 0. Set the selected item. Don't use setSelectedItem() because
		////    that has additional behavior.
		selected = -1;

		//// 1.  First, recurse to the top window so we can attach ourself
		////     to it.
		Container parent = null;
		Window parentWindow = null;

		if (invoker != null) {
			parent = invoker.getParent();
		}

		Point pt = invoker.getLocation();
		Point ptTmp;

		for (Container p = parent; p != null; p = p.getParent()) {
			//// 2.1. Get to the top of the rootpane.
			if (p instanceof JRootPane) {
				parent = ((JRootPane) p).getLayeredPane();
				p = parent.getParent();
				while (p != null && !(p instanceof java.awt.Window)) {
					p = p.getParent();
				}
				parentWindow = (Window) p;
				break;
			}
			//// 2.2. Get to the top Java Window.
			else if (p instanceof Window) {
				throw new RuntimeException("Sorry, Sunburst Menu does not work with non-Swing widgets yet");
				// parent       = p;
				// parentWindow = (Window) p;
				// break;
			}
			ptTmp = p.getLocation();
			pt.x += ptTmp.x;
			pt.y += ptTmp.y;
		}

		//// 3. Setup our location and layer on the layered pane.
		debug("point.x is " + pt.x + ", point.y is " + pt.y);
		int fixX = fixPieLocationX(pt.x + x);
		int fixY = fixPieLocationY(pt.y + y);
		debug("fixX is " + fixX + ", fixY is " + fixY);

		setLocation(
			fixX - getTreeDepth() * radius / 2 - radius,
			fixY - getTreeDepth() * radius / 2 - radius);
		//setLocation(x - 200, y - 200);
		if (parent instanceof JLayeredPane) {
			((JLayeredPane) parent).add(this, JLayeredPane.POPUP_LAYER, 0);
		} else {
			parent.add(this);
		}

		//// 4.1. Setup stuff on our parent.
		this.parent = parent;

		//// 4.2. No submenu showing.
		//flagCanAbortSubmenu = true;
		flagJustOpened = true;
		//flagJustClosedSubmenu = false;
		flagDraggedInPieMenu = false;
		if (showThread != null) {
			showThread.abort();
			showThread = null;
		}

		//we don't worry about submenus for now
		/*if (submenuThread != null) {
			submenuThread.abort();
			submenuThread       = null;
			submenu             = null;
		}*/

		//// 5. Fire off notifications.
		setVisible(true);
	} // of method

	//-----------------------------------------------------------------

	//Helper functions that exist only to help adjust where the pie menus
	//are placed relative to screen boundaries

	//TODO: I need to modify the following function to make it work
	//for the Sunburst menu

	private int fixPieLocationX(int x) {
		Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
		int returnvalue = x;
		if (x + defaultBigRadius > dim.width) {
			returnvalue = dim.width - defaultBigRadius;
		}
		if (x - defaultBigRadius < 0) {
			returnvalue = defaultBigRadius;
		}
		return returnvalue;

	}

	private int fixPieLocationY(int y) {
		Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
		int returnvalue = y;
		if (y + defaultBigRadius > dim.height) {
			returnvalue = dim.height - defaultBigRadius;
		}
		if (y - defaultBigRadius < 0) {
			returnvalue = defaultBigRadius;
		}
		return returnvalue;
	}
	//Ends here...

	/**
	 * draw tool tips is not very object oriented, but
	 * it's a hack for now. This will draw multiline 
	 * tooltips on the screen for the user
	 */
	public void drawTextWithBox(
		Graphics2D g,
		Font font,
		int x,
		int y,
		String s) {
		String text = s;
		StringTokenizer st = new StringTokenizer(text, "\n");
		ArrayList lines = new ArrayList();
		while (st.hasMoreTokens()) {
			lines.add(st.nextToken());
		}

		//get the font size first for the default font
		FontMetrics fontSize = g.getFontMetrics(font); // Checking the

		//then set the first starting point to be the object top right screen position
		int xs[] = new int[lines.size()];
		int ys[] = new int[lines.size()];

		xs[0] = x;
		ys[0] = y;

		String line = (String) lines.get(0);

		int totalWidth = fontSize.stringWidth(line);
		int height = fontSize.getHeight();

		for (int j = 0; j < lines.size() - 1; j++) {
			line = (String) lines.get(j);
			int tempWidth = fontSize.stringWidth(line);
			if (tempWidth > totalWidth)
				totalWidth = tempWidth;
			xs[j + 1] = xs[0];
			ys[j + 1] = ys[j] + height;
		}

		int totalHeight = height * lines.size();

		// size of the font.
		//int width = fontSize.stringWidth(t.getToolTipText());
		// Computing the size of the box in which
		// the string

		g.setColor(Color.black);
		g.fillRect(xs[0], ys[0], totalWidth + 2, totalHeight + 2);
		g.setColor(Color.yellow);
		g.fillRect(xs[0] + 1, ys[0] + 1, totalWidth, totalHeight);
		g.setColor(Color.black);
		//g.setFont(Globals.DEFAULT_TIP_FONT);
		for (int k = 0; k < lines.size(); k++) {
			g.drawString((String) lines.get(k), xs[k] + 1, ys[k] + height - 1);
		}
	}

	int MODE_BOX = 1;
	int MODE_PLAIN = 2;

	public void renderString(
		Graphics2D gg,
		SunburstMenuItem item,
		int level,
		boolean enabled,
		int mode) {

		Arc2D arc = item.getArc();
		String str = item.getText();

		double angle = arc.getAngleStart() + arc.getAngleExtent() / 2.0;
		//let's do this: first, we will find the mid line, and find it's cross point 
		//with the arc, and linearly interpolate it with proportional to the cell's
		//level, and once this point is found, we render string over there. 

		Rectangle rect = arc.getBounds();

		Arc2D halfArc =
			new Arc2D.Double(
				rect,
				arc.getAngleStart(),
				arc.getAngleExtent() / 2.0,
				Arc2D.PIE);

		int maxLevel = moGraphicModel.getMaxDepth();
		level = level + 1;

		double centerX = (double) (maxLevel * radius + 2 * radius) / 2.0;
		double centerY = centerX;
		Point2D endPoint = halfArc.getEndPoint();

		int base = maxLevel;

		int x =
			(int) ((centerX * (1.0 - mdRelativeStringPosition[level]))
				+ (endPoint.getX()) * mdRelativeStringPosition[level]);

		int y =
			(int) ((centerY * (1.0 - mdRelativeStringPosition[level]))
				+ (endPoint.getY()) * mdRelativeStringPosition[level]);

		gg.setColor(Color.black);

		Point2D textLocation = new Point2D.Float(x, y);
		item.setStringPoint(textLocation);

		//Simple Render String will render the string according to it's 
		//rotational angle, what if I don't worry about the angle
		//and always render string horizontally
		if (mode == MODE_PLAIN) {
			simpleRenderString(gg, x, y, str, angle, enabled, true);
		}

		//renderStringWithBox will draw the string with box and the
		//alignment is better
		if (mode == MODE_BOX)
			renderStringWithBox(gg, x, y, str, angle, enabled);

		//The following method will draw text with yellow box, but the alignment
		//has some problems
		//drawTextWithBox(gg, this.DEFAULT_FONT, x, y, str);
	}

	/**
	 * render the string simply at the mid point of the 
	 * arc curve. if rotate is enabled, then it will rotate
	 * according to the angle of the arc, if not, it 
	 * will render everything horizontally
	 * 
	 * @param gg
	 * @param xx
	 * @param yy
	 * @param str
	 * @param angle
	 * @param enabled
	 * @param rotate
	 */
	protected void simpleRenderString(
		Graphics2D gg,
		int xx,
		int yy,
		String str,
		double angle,
		boolean enabled,
		boolean rotate) {

		//// 1. Compute the width of the string.
		Point pt = new Point(); // temporary storage
		FontMetrics fmetric = getFontMetrics(getFont());
		// current font metrics
		float width; // width is variable
		float height = fmetric.getHeight(); // height is constant

		StringTokenizer strtok = new StringTokenizer(str, "\r\n\t");
		int offset = 0;
		String token;

		//System.out.println(angle);

		while (strtok.hasMoreTokens()) {
			token = strtok.nextToken();
			width = fmetric.stringWidth(token);

			//the rotation here will align the text font
			if (rotate) {
				gg.rotate(-Math.toRadians(angle), xx, yy + offset);
			}
			if (enabled == true) {
				gg.setColor(getFontColor());
				gg.drawString(token, (int) xx, (int) yy + offset);

				//// Draw a bounding box around the text for debugging.
				//gg.drawRect((int) (pt.x - 0.5*width), (int) (pt.y - 0.5*height +offset),
				//            (int) width, (int) height);
			} else {
				gg.setColor(getFillColor().darker());
				gg.drawString(token, (int) xx, (int) yy + offset);
				//// Draw a bounding box around the text for debugging.
				//gg.drawRect((int) (pt.x - 0.5*width), (int) (pt.y - 0.5*height +offset),
				//            (int) width, (int) height);
			}
			if (rotate) {
				gg.rotate(Math.toRadians(angle), xx, yy + offset);
			}
			offset += height;
		}

	}

	protected void renderStringWithBox(
		Graphics2D gg,
		int xx,
		int yy,
		String str,
		double angle,
		boolean enabled) {

		//// 1. Compute the width of the string.
		//int pt = new Point(); // temporary storage
		FontMetrics fmetric = getFontMetrics(getFont());
		// current font metrics
		float width; // width is variable
		float height = fmetric.getHeight(); // height is constant

		StringTokenizer strtok = new StringTokenizer(str, "\r\n\t");
		int offset = 0;
		String token;

		//System.out.println(angle);

		//determine which quarter this angle belongs to 
		//this will create 4 quarters: top right 0
		//top left 1, bottom left, 2, bottom right,3
		int mode = (int) ((angle / 45.0) % 8.0);

		while (strtok.hasMoreTokens()) {
			token = strtok.nextToken();
			width = fmetric.stringWidth(token);

			int xT = 0, yT = 0;

			switch (mode) {
				case 0 : //o-
					xT = xx;
					yT = (int) (yy + offset + height / 2);
					break;
				case 1 : //o/
					xT = xx;
					yT = (int) (yy + offset);
					break;
				case 2 : //|
					//o
					xT = (int) (xx - width / 2);
					yT = (int) (yy + offset - height / 2);
					break;
				case 3 : //\o
					xT = (int) (xx - width);
					yT = (int) (yy + offset);
					break;
				case 4 : //-o
					xT = (int) (xx - width);
					yT = (int) (yy + offset);
					break;
				case 5 : // /o
					xT = (int) (xx - width);
					yT = (int) (yy + offset + height);
					break;
				case 6 : //o
					//|
					xT = (int) (xx - width / 2);
					yT = (int) (yy + offset + height);
					break;
				case 7 : //o\
					xT = xx;
					yT = (int) (yy + offset + height);
			}

			if (enabled == true) {
				gg.setColor(getFontColor());
				//gg.drawString(token, xT, yT);
				this.drawTextWithBox(gg, getFont(), xT, yT, token);

				//// Draw a bounding box around the text for debugging.
				//gg.drawRect((int) (pt.x - 0.5*width), (int) (pt.y - 0.5*height +offset),
				//            (int) width, (int) height);
			} else {
				gg.setColor(getFillColor().darker());
				gg.drawString(token, xT, yT);
				//// Draw a bounding box around the text for debugging.
				//gg.drawRect((int) (pt.x - 0.5*width), (int) (pt.y - 0.5*height +offset),
				//            (int) width, (int) height);
			}

			offset += height;
		}

	}

	/**
	   * Render a String at the given angle and maximum radius.
	   *
	   * @param gg      is the Graphics context to render on.
	   * @param xx      is the origin for the angle and radius.
	   * @param yy      is the origin for the angle and radius.
	   * @param str     is the String to render.
	   * @param angle   is the angle to render the String str at.
	   * @param radius  is the largest radius to render the String str at.
	   *                The pie menu scaling factor will be used.
	   * @param enabled specifies whether the menu item is enabled or not.
	   */
	protected void renderString(
		Graphics2D gg,
		int xx,
		int yy,
		String str,
		double angle,
		double radius,
		boolean enabled) {

		//// 1. Compute the width of the string.
		Point pt = new Point(); // temporary storage
		FontMetrics fmetric = getFontMetrics(getFont());
		// current font metrics
		float width; // width is variable
		float height = fmetric.getHeight(); // height is constant

		//// 2. Convert the coordinates from polar to cartesian.
		polarToCartesian(xx, yy, angle, radius * scalingFactor, pt);

		//// 2.1. Readjust the point so it's actually in the center of the pie
		////      menu.
		Point m_pt = new Point();
		polarToCartesian(xx, yy, angle, radius * scalingFactor, m_pt);
		m_pt.x = m_pt.x + DEFAULT_BIG_RADIUS;
		m_pt.y = m_pt.y + DEFAULT_BIG_RADIUS;

		//// 3. Tokenize the String, in case it has newlines and such.
		StringTokenizer strtok = new StringTokenizer(str, "\r\n\t");
		int offset = 0;
		String token;

		//System.out.println(angle);

		//// 3.1. Draw each token so that it is centered at pt.x and pt.y.
		pt.y = pt.y - (int) (((float) (strtok.countTokens() - 1) / 2) * height);

		//		 added by Shen, try set the rotation here for the String
		//AffineTransform at = new AffineTransform();
		//at.setToRotation(Math.toRadians(45));
		//gg.setTransform(at);

		while (strtok.hasMoreTokens()) {
			token = strtok.nextToken();
			width = fmetric.stringWidth(token);
			//System.out.println(Math.sin(angle));

			//// 3.1.1. Figure out the orientation, and use it to scoot the text
			////        over on the pie menus.
			double orientation = findAngle(angle);
			//System.out.println(orientation);

			if (orientation == ORIENT_TOP) {
				m_pt.y = (int) (-1 * DEFAULT_BIG_RADIUS * Math.sin(angle));
				// - 0.5*height);
			}

			gg.rotate(
				-Math.toRadians(angle),
				(pt.x - 0.5 * width),
				(pt.y + 0.5 * height) + offset);
			if (enabled == true) {
				gg.setColor(getFontColor());
				gg.drawString(
					token,
					(int) (pt.x - 0.5 * width),
					(int) (pt.y + 0.5 * height) + offset);

				//// Draw a bounding box around the text for debugging.
				//gg.drawRect((int) (pt.x - 0.5*width), (int) (pt.y - 0.5*height +offset),
				//            (int) width, (int) height);
			} else {
				gg.setColor(getFillColor().darker());
				gg.drawString(
					token,
					(int) (pt.x - 0.5 * width),
					(int) (pt.y + 0.5 * height) + offset);
				//// Draw a bounding box around the text for debugging.
				//gg.drawRect((int) (pt.x - 0.5*width), (int) (pt.y - 0.5*height +offset),
				//            (int) width, (int) height);
			}
			gg.rotate(
				Math.toRadians(angle),
				(pt.x - 0.5 * width),
				(pt.y + 0.5 * height) + offset);
			offset += height;
		}

		//at.setToRotation(-Math.toRadians(angle));
		//gg.setTransform(at);

		//gg.setColor(Color.green);
		//gg.drawRect((int)m_pt.x, (int)m_pt.y, 50, 50);
		//System.out.println("X-value: " + m_pt.x + " Y value: " + m_pt.y);

	} // of method

	private int findAngle(double angle) {
		double newAngle = angle % (Math.PI * 2);
		//System.out.println(newAngle);
		if (newAngle > Math.PI / 4 && newAngle < Math.PI * 0.75) {
			return ORIENT_TOP;
		} else if (
			newAngle < Math.PI / 4
				&& newAngle > 0) { //|| newAngle > Math.PI *1.75)
			return ORIENT_TOPRIGHT;
		} else if (newAngle > Math.PI * 1.75) {
			return ORIENT_BOTRIGHT;
		} else if (newAngle < Math.PI * 1.75 && newAngle > Math.PI * 1.25) {
			return ORIENT_BOTTOM;
		} else if (newAngle < Math.PI && newAngle > Math.PI * 0.75) {
			return ORIENT_TOPLEFT;
		} else if (newAngle < Math.PI * 1.25 && newAngle > Math.PI) {
			return ORIENT_BOTLEFT;
		} else {
			return ORIENT_TOP;
		}
	}

	/**
	  * Convert polar coordinates to cartesian.
	  *
	  * @param  x       is the x-coordinate of the origin.
	  * @param  y       is the y-coordinate of the origin.
	  * @param  radians is the number of radians to go through.
	  * @param  radius  is the length of the radius.
	  * @param  pt      is the point to put the results in.
	  * @return an (x,y) point.
	  */
	Point polarToCartesian(
		int x,
		int y,
		double radian,
		double radius,
		Point pt) {

		//// 1. Convert the polar coordinates.
		////    Normally, the y calculation would be addition instead of
		////    subtraction, but we are dealing with screen coordinates,
		////    which has its y-coordinate at the top and not the bottom.
		pt.x = (int) (x + radius * Math.cos(radian));
		pt.y = (int) (y - radius * Math.sin(radian));

		//// 2. Straighten up the data a little.
		if (Math.abs(pt.x - radius) <= 1) {
			pt.x = (int) radius;
		}
		if (Math.abs(pt.y - radius) <= 1) {
			pt.y = (int) radius;
		}

		return (pt);
	} // of method

	/**
	 * I want to create a method here to handle the expanding target
	 * this is the function that takes input from the mouse, and reformat
	 * the menu component, it will rescale, or expand the menu item according 
	 * to the distance of the mouse of pen that's close to border of the menu item
	 * and expand all the children of that node. 
	 *
	 */
	public void expandNodes(int x, int y) {

		//1. get the center of the circle
		int maxLevel = moGraphicModel.getMaxDepth();
		double centerX = (double) (maxLevel * radius + 2 * radius) / 2.0;
		double centerY = centerX;
		double xx = x - centerX;
		double yy = y - centerY;

		//2. calculate the distance of the point to the center
		double distance = Math.sqrt(xx * xx + yy * yy);

		SunburstMenuItem item = moGraphicModel.pickItem(x, y);
		if (item != null) {
			ArrayList items = item.getAllChildrenByLevel();
			for (int i = 0; i < items.size(); i++) {
				ArrayList aLevel = (ArrayList) items.get(i);
				//items[i].setHighlight(true);
				for (int j = 0; j < aLevel.size(); j++) {
					SunburstMenuItem anItem = (SunburstMenuItem) aLevel.get(j);
					anItem.getArc().setAngleExtent(
						anItem.getAngleExtent() * 2.0);
					anItem.getArc().setAngleStart(
						anItem.getAngleStart() + anItem.getAngleExtent() * j);
				}
			}
			this.repaintBounds();
		}
	}

	/**
	 * Do the formating of the Sunburst menu component
	 * this need to be taken out of the paintComponent method
	 * because this will be only done once, and we don't 
	 * want to calculate this everything when repaint is called
	 *
	 */
	public void formatComponent() {

		if (moGraphicModel == null)
			moGraphicModel = new TreeGraphicModel(list, moMarkingMenu);

		int numItems = this.getItemCount();
		double stepRadian = 2 * Math.PI / numItems;
		double currentRadian = getStartRadian();
		Shape oldClip = null;
		TreeModel item;
		String text;
		Icon icon;
		Arc2D arc;

		//// I will try to format the Sunburst menu first, and then
		//// draw it later
		int depth = moGraphicModel.getMaxDepth();
		// we don't count the root because it only has one node

		double currentMarkingRadian = getStartRadian();
		double stepMarkingRadian =
			2.0 * Math.PI / (double) moMarkingMenu.childCount();
		Arc2D mArc;
		//G.debug("Step marking radian is " + stepMarkingRadian);
		//Here, we will add the marking menu part here
		for (int i = 0; i < moMarkingMenu.childCount(); i++) {
			mArc =
				new Arc2D.Float(
					(depth + 1) * radius / 2 - 20,
					(depth + 1) * radius / 2 - 20,
					radius + 40,
					radius + 40,
					(float) Math.toDegrees(currentMarkingRadian),
					(float) Math.toDegrees(stepMarkingRadian),
					Arc2D.PIE);
			SunburstMenuItem mkItem = new SunburstMenuItem(mArc);
			mkItem.setText(moMarkingMenu.getChild(i).getTitle());
			moGraphicModel.addToMarkingMenu(mkItem);
			currentMarkingRadian += stepMarkingRadian;
		}

		//I try to increase the marking circle by 40 here
		moGraphicModel.getMarkingCircle().setFrame(
			(depth + 1) * radius / 2 - 20,
			(depth + 1) * radius / 2 - 20,
			radius + 40,
			radius + 40);

		//// 1.1. For each element, draw a line from (radius, radius) to
		////      the endpoint.
		//gg.setColor(getLineColor());
		for (int i = 0; i < numItems; i++) {
			//// 1.1.1. Calculate the arc size.
			arc =
				new Arc2D.Float(
					depth * radius / 2,
					depth * radius / 2,
					2 * radius,
					2 * radius,
					(float) Math.toDegrees(currentRadian),
					(float) Math.toDegrees(stepRadian),
					Arc2D.PIE);
			SunburstMenuItem mItem = new SunburstMenuItem(arc);
			item = list.getChild(i);
			mItem.setText(item.getTitle());

			moGraphicModel.addShapeToLayer(0, mItem);
			//added the children directly to the root to the first layer
			if (moGraphicModel.getCircle(0) == null)
				moGraphicModel.addCircleToLayer(0, new Ellipse2D.Double());
			//CIRCLES[0] = new Ellipse2D.Double();
			moGraphicModel.getCircle(0).setFrame(
				depth * radius / 2,
				depth * radius / 2,
				2 * radius,
				2 * radius);

			//// 1.1.2. Color in the selected item.

			//// 1.1.5. Retrieve the menu element and subparts.
			////        This part is hackish, and will be until PieMenu
			////        implements the full JMenu interface.

			//text = getItemText(item, i);
			//icon = getItemIcon(item, i);

			if (item.childCount() > 0) {
				// Draw the next level of things, and recursively
				formatSubMenu(
					item,
					mItem,
					currentRadian,
					stepRadian,
					3 * radius,
					depth - 1);
			}

			//// 1.1.9. Prepare for the next iteration / pie slice.
			currentRadian += stepRadian;
		} // of for

	}

	public void formatSubMenu(
		TreeModel list,
		SunburstMenuItem parent,
		double currentRadian,
		double stepRadian,
		int newRadius,
		int depth) {
		Arc2D arc;
		int subMenuSize = list.childCount();
		double subStepRadian = stepRadian / ((double) subMenuSize);
		int index = moGraphicModel.getMaxDepth() - depth;
		//System.out.println("Depth is" + index);
		if (moGraphicModel.getCircleSet(index) == false) {
			//moGraphicModel.getCircle(index) = new Ellipse2D.Double();
			moGraphicModel.getCircle(index).setFrame(
				depth * radius / 2,
				depth * radius / 2,
				newRadius,
				newRadius);
			moGraphicModel.setCircleSet(index, true);
		}

		for (int i = 0; i < subMenuSize; i++) {
			arc =
				new Arc2D.Float(
					radius * depth / 2,
					radius * depth / 2,
					newRadius,
					newRadius,
					(float) Math.toDegrees(currentRadian),
					(float) Math.toDegrees(subStepRadian),
					Arc2D.PIE);

			SunburstMenuItem mItem = new SunburstMenuItem(arc);
			TreeModel subItem = list.getChild(i);
			//temporary hack
			mItem.setText(subItem.getTitle());
			parent.addChild(mItem);
			//System.out.println("currentRadian = " + currentRadian + " subStepRadian = " + subStepRadian);
			moGraphicModel.getLayer(index).add(mItem);
			//gg.draw(arc);						  

			if (subItem.childCount() > 0) {
				formatSubMenu(
					subItem,
					mItem,
					currentRadian,
					subStepRadian,
					newRadius + radius,
					depth - 1);
			}

			currentRadian += subStepRadian;
		}
	}

	/**
	 * The way I am doing the painting right now is 
	 * First, walk through the tree, and create all the formating
	 * of the tree elements. When we do the tree walk, we go from 
	 * root to the leaves, but when we paint the arcs, we are going from
	 * leaves to the roots, so this is the difference. 
	 * 
	 * Before we do the calculation, we need to know the maximum depth
	 * of the tree, so we can create the proper number of storage for the arcs, and 
	 * circles.
	 * We then calculate arc for each of the nodes, once that's done
	 * we draw the outmost circle first, then the outmost arcs, and then we draw the inner circle
	 * then the inner arcs recursively. We need to draw a small cicle in the end as the root area
	 */
	public void paintComponent(Graphics g) {
		//// 0.1. Cast as Graphics2D.
		Graphics2D gg = (Graphics2D) g;
		Color oldc = g.getColor();

		//// 0.2. Should we do clipping?
		//if (getAllClipping() == true) {
		//	gg.setClip(clipCircle);
		//}

		//// 0.3. Set the rendering quality to make it higher.
		Map oldHints = (Map) gg.getRenderingHints();
		gg.setRenderingHints(newHints);

		//// 0.4. Set the rendering attributes.
		////      Font is set automatically for us. Stroke is not. Why?
		////      Who knows? It's certainly a strange design decision.
		gg.setStroke(stroke);
		gg.setPaintMode();

		//// 0.5. Get the selected item number so we can highlight it later.
		int selectedItem = getSelectedItem(); //getSelectedItem();
		int defaultSelectedItem = 1; //getDefaultSelectedItem();

		int treeDepth = moGraphicModel.getMaxDepth();
		//// 1. Draw the boundary lines. Do nothing if no elements.
		////    First, calculate the radians required by each element.
		int numItems = list.childCount();
		if (numItems <= 0) {
			gg.setColor(getFillColor());
			gg.fillOval(0, 0, 2 * radius, 2 * radius);
		} else {

			//We first draw the Sunburst
			for (int i = treeDepth - 1; i > -1; i--) {
				gg.setColor(getSelectedColor());

				gg.fill(moGraphicModel.getCircle(i));

				ArrayList layer = moGraphicModel.getLayer(i);
				for (int j = 0; j < layer.size(); j++) {

					if (layer.get(j) != null) {
						SunburstMenuItem item = (SunburstMenuItem) layer.get(j);
						//try to do different things for highlighted items
						if (item.isHighlight()) {
							gg.setColor(Color.GREEN);
							gg.fill(item.getArc());
							gg.setColor(DEFAULT_LINE_COLOR);
							gg.draw(item.getArc());
						} else {
							gg.setColor(DEFAULT_LINE_COLOR);
							gg.draw(item.getArc());
						}
					}
				}
			}

			//before drawing the marking menu, try to draw the marking menu circle
			gg.setColor(this.getBackground());
			gg.fill(moGraphicModel.getMarkingCircle());
			//gg.draw(moGraphicModel.getMarkingCircle());

			//let's draw the marking menu part
			gg.setColor(DEFAULT_LINE_COLOR);
			ArrayList markingMenu = moGraphicModel.getMarkingMenu();
			for (int i = 0; i < markingMenu.size(); i++) {

				SunburstMenuItem mItem = (SunburstMenuItem) markingMenu.get(i);
				//// 1.1.2. Color in the selected item.
				if (selectedItem == i) {
					gg.setColor(getSelectedColor());
					gg.fill(mItem.getArc());
				}
				//// 1.1.3. Otherwise, color in the normal fill color.
				else {
					gg.setColor(DEFAULT_LINE_COLOR);
					gg.draw(mItem.getArc());

					//// 1.1.4. Color in the default selected item.
					/*if (defaultSelectedItem == i) {
						gg.setColor(defaultSelectedItemColor);
						gg.fill(mItem.getArc());
					}*/
				}

				//gg.draw(mItem.getArc());
			}

			//Draw the root Circle ***************
			Ellipse2D rootCircle = new Ellipse2D.Double();
			rootCircle.setFrame(
				treeDepth * radius / 2 + radius - smallRadius,
				treeDepth * radius / 2 + radius - smallRadius,
				2 * smallRadius,
				2 * smallRadius);

			smallCircle = rootCircle;

			gg.setColor(getFillColor());
			gg.fill(rootCircle);
			if (selectedItem < 0) {
				gg.setColor(getSelectedColor());
				gg.fill(rootCircle);
			}

			//// 2.2. Draw the line around the small circle in the center.
			gg.setColor(getLineColor());
			gg.draw(rootCircle);

			//**************** rendering string ********************************

			//			then render the string on the top
			for (int i = treeDepth - 1; i > -1; i--) {
				gg.setColor(getSelectedColor());

				//gg.fill(moGraphicModel.getCircle(i));

				ArrayList layer = moGraphicModel.getLayer(i);
				for (int j = 0; j < layer.size(); j++) {

					gg.setColor(DEFAULT_LINE_COLOR);
					if (layer.get(j) != null) {
						SunburstMenuItem item = (SunburstMenuItem) layer.get(j);

						this.renderString(gg, item, i, true, MODE_PLAIN);

					}
				}
			}

			//**************** rendering string link ********************************

			//			then render the string on the top
			/*for (int i = treeDepth - 1; i > -1; i--) {
				gg.setColor(getSelectedColor());

				ArrayList layer = moGraphicModel.getLayer(i);
				for (int j = 0; j < layer.size(); j++) {

					gg.setColor(this.DEFAULT_LINE_COLOR);
					if (layer.get(j) != null) {
						SunburstMenuItem item = (SunburstMenuItem) layer.get(j);
						for (int k = 0; k < item.getChildCount(); k++) {
							SunburstMenuItem subItem = item.getChildAt(k);
							if (subItem != null)
								gg.drawLine(
									(int) item.getStringPoint().getX(),
									(int) item.getStringPoint().getY(),
									(int) subItem.getStringPoint().getX(),
									(int) subItem.getStringPoint().getY());
							else {
								G.debug("null");
							}
						}
					}
				}
			}*/

			//************** finally, render Strings for marking menu ********
			for (int i = 0; i < markingMenu.size(); i++) {
				SunburstMenuItem mItem = (SunburstMenuItem) markingMenu.get(i);
				gg.setFont(DEFAULT_MARKING_FONT);

				//remember, for marking menu, the level is -1, so after add 1, it
				//will be 0
				this.renderString(gg, mItem, -1, true, MODE_PLAIN);

				//in the end, I need to reset everything to normal, turn the highlight off
				// and everything else off
				moGraphicModel.resetAllMenuItems();
			}
		} // of method
	}

	public int getTreeDepth() {
		if (moGraphicModel != null)
			return moGraphicModel.getMaxDepth();
		else
			return 0;
	}

	/**
	 * This method construct the sunburst menu
	 * from the treemodel we parsed from xml file
	 * and return to the user
	 * 
	 * The tree model has two nodes:
	 * 1. Sunburst menu
	 * 		Sunburst SubMenu
	 * 		Sunburst Submenu
	 * 		...
	 * 
	 * 2. marking menu
	 * 
	 * @param m1
	 * @return
	 */
	public static SunburstMenuNoExpansionRotate getMenuFromTree(TreeModel m1) {

		ArrayList list = new ArrayList();

		for (int i = 0; i < m1.getChild(0).childCount(); i++) {
			list.add(m1.getChild(0).getChild(i));
		}

		SunburstMenuNoExpansionRotate menu = new SunburstMenuNoExpansionRotate(list, m1.getChild(1));
		return menu;
	}

	static String[] msMarkingMenuStr =
		{
			"File",
			"Edit",
			"Source",
			"Refactor",
			"Navigate",
			"Search",
			"Project",
			"Run",
			"Window",
			"Help" };

	public static void main(String[] args) {
		//SunburstMenu menu = new SunburstMenu();

		TreeModel tree = new TreeModel("Move");

		//MapLayout algorithm = new StripTreemap();

		for (int i = 0; i < 10; i++) {
			TreeModel model1 = new TreeModel("" + i + ",0");
			for (int j = 0; j < 3; j++) {
				model1.addChild(new TreeModel("" + i + "," + j));
			}
			//algorithm.layout(model1, model1.getMapItem().getBounds());
			tree.addChild(model1);
		}

		TreeModel tree1 = new TreeModel("Open");
		TreeModel tree2 = new TreeModel("New");
		TreeModel subTree1 = new TreeModel("Open Special");
		TreeModel subTree2 = new TreeModel("Save");
		TreeModel subTree3 = new TreeModel("Save as");
		tree1.addChild(subTree1);
		tree1.addChild(subTree2);
		tree1.addChild(subTree3);

		ArrayList treeModels = new ArrayList();
		treeModels.add(tree);
		treeModels.add(tree1);
		treeModels.add(tree2);

		//create the marking menu here
		TreeModel markingMenuModel = new TreeModel();

		for (int i = 0; i < msMarkingMenuStr.length; i++) {
			TreeModel model2 = new TreeModel(msMarkingMenuStr[i]);
			markingMenuModel.addChild(model2);
		}

		//SunburstMenu menu = new SunburstMenu(treeModels, markingMenuModel);

		//Here, let me do something with the sunburst loader
		SaxSunburstLoader ssl = new SaxSunburstLoader();
		ssl.loadMenus(args[0]);

		SunburstMenuNoExpansionRotate menu = getMenuFromTree(ssl.getDefaultTreeModel());

		//menu.setMenuSize(menu.getTreeDepth());

		menu.formatComponent();
		debug("tree depth is " + menu.getTreeDepth());

		JFrame frame = new JFrame("Sunburst");

		frame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent evt) {
				System.exit(0);
			}
		});

		JPanel panel = new JPanel();
		//frame.getContentPane().add(menu);
		menu.setPenMode();
		menu.addSunburstMenuTo(frame.getContentPane());

		frame.setSize(new Dimension(600, 600));
		frame.show();
	}

}
